﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using StephenEssential;


namespace MinesweeperAuto
{
    class Player
    {
        private readonly ChessBoard _chessBoard;
        private readonly ChessBoard _currentBoard;
        private readonly HashSet<Point> _currentPoints; 

        private enum EState
        {
            Well,
            Failed,
            Bored,
            FlagAllUnknown,
            ClickAllUnknown
        }

        public Player(int width, int height, int mineCount)
        {
            _currentPoints = new HashSet<Point>();
            _chessBoard = new ChessBoard(width, height, mineCount);
            _currentBoard = new ChessBoard(width, height);
        }

        private void Playing(Point beginPoint)
        {
            Click(beginPoint);
            Expanding(beginPoint);
            _currentBoard.SelectWhile((g, x, y) => new Point(x, y),
                g => g is ChessBoard.Flat && (g as ChessBoard.Flat).SurroundingMineCount > 0)
                .ForEach(p => _currentPoints.Add(p));
            while (true)
            {
                _currentPoints.ForEach(currentPoint =>
                {
                    switch (AnalysisSingleGrid(currentPoint))
                    {
                        case EState.Bored:
                            _currentPoints.Remove(currentPoint);
                            return;
                        case EState.FlagAllUnknown:
                            _currentBoard.GetNeiborPoints(currentPoint, p => _currentBoard[p] is ChessBoard.Unknown).ForEach(p =>
                            {
                                if (_chessBoard[p] is ChessBoard.Mine) _currentBoard[p] = new ChessBoard.Mine();
                                else throw new MineSweeperException(p);
                            });
                            return;
                        case EState.ClickAllUnknown:
                            _currentBoard.GetNeiborPoints(currentPoint, p => _currentBoard[p] is ChessBoard.Unknown).ForEach(
                                p =>
                                {
                                    Click(p);
                                    Expanding(p);
                                });
                            return;
                        default:
                            throw new NotImplementedException();
                    }
                });
            }
        }



        private EState AnalysisSingleGrid(Point currentPoint)
        {
            var currentGrid = _currentBoard[currentPoint];
            if (currentGrid is ChessBoard.Unknown) Debugger.Break();
            if (currentGrid is ChessBoard.Flat)
            {
                var flatGrid = currentGrid as ChessBoard.Flat;
                if (!_currentBoard.GetNeiborPoints(currentPoint, p => _currentBoard[p] is ChessBoard.Unknown).Any()) return EState.Bored;
                if (
                    _currentBoard.GetNeiborPoints(currentPoint,
                        p =>
                            _currentBoard[p].Pipe(
                                gridType => gridType is ChessBoard.Mine || gridType is ChessBoard.Unknown)).Count ==
                    flatGrid.SurroundingMineCount) return EState.FlagAllUnknown;
                if (_currentBoard.GetNeiborPoints(currentPoint, p => _currentBoard[p] is ChessBoard.Mine).Count ==
                    flatGrid.SurroundingMineCount) return EState.ClickAllUnknown;


            }
//            

            throw new NotImplementedException();
        }

        private void Click(Point currentPoint)
        {
            if (_chessBoard[currentPoint] is ChessBoard.Mine) throw new MineTriggerException(currentPoint);
            _currentBoard[currentPoint] = _chessBoard[currentPoint];
            _currentPoints.Add(currentPoint);
        }

        private void Expanding(Point currentPoint)
        {
            if (!(_currentBoard[currentPoint] is ChessBoard.Flat) ||
                (_currentBoard[currentPoint] as ChessBoard.Flat).SurroundingMineCount != 0) return;
            _currentBoard.GetNeiborPoints(currentPoint, p => true).ForEach(p =>
            {
                Click(p);
                Expanding(p);
            });
        }

        

    }

    class MineTriggerException : MineSweeperException
    {
        public MineTriggerException(Point sourcePoint) : base(sourcePoint) { }

        public override string Message
        {
            get { return String.Format("Misclick in Position: {0}", SourcePoint); }
        }
    }

    class MineSweeperException : Exception
    {
        protected readonly Point SourcePoint;

        public MineSweeperException(Point sourcePoint)
        {
            SourcePoint = sourcePoint;
        }

        public override string Message
        {
            get { return String.Format("Error From {0}", SourcePoint); }
        }
    }



    
}
